﻿using Canteen.Core.Common;
using Canteen.Core.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace Canteen.Core.Services
{
    public class UserServiceImplement : IUserService
    {
        #region 构造函数
        private readonly CanteenDbContext dbContext;
        private readonly HttpContext httpContext;
        private readonly IRedisCacheManager redisCacheManager;
        private readonly IJwtAuth jwtAuth;
        private readonly IGetting getting;

        public UserServiceImplement(CanteenDbContext canteenDbContext,
            IHttpContextAccessor httpContext,
            IRedisCacheManager redisCacheManager,
            IJwtAuth jwtAuth,
            IGetting getting)
        {
            this.dbContext = canteenDbContext ?? throw new ArgumentNullException(nameof(canteenDbContext));
            this.httpContext = httpContext?.HttpContext ?? throw new ArgumentNullException(nameof(httpContext));
            this.redisCacheManager = redisCacheManager ?? throw new ArgumentNullException(nameof(redisCacheManager));
            this.jwtAuth = jwtAuth ?? throw new ArgumentNullException(nameof(jwtAuth));
            this.getting = getting ?? throw new ArgumentNullException(nameof(getting));
        }
        #endregion

        private T RedisCache<T>(Func<IDatabase, T> func)
        {
            return func(redisCacheManager.RedisCache(c => c, 6));
        }

        public async Task<User> CreateUser(User user)
        {
            //1 通过<手机号码>查询[用户信息]
            if (await dbContext.Users.Where(u => u.Phone == user.Phone).CountAsync() > 0) throw new Exception("手机号码已经存在");

            user.Logins = 1;
            user.Wallet = 0;
            await dbContext.Users.AddAsync(user);
            if (await dbContext.SaveChangesAsync() > 0) return user;
            throw new Exception("保存用户信息失败");
        }
        public async Task<User> UpdateUser(User user)
        {
            //先查询出来，因为操作被追踪的数据
            var entity = await dbContext.Users.SingleOrDefaultAsync(s => s.Id == user.Id);
            //修改Name属性，被追踪的league状态属性就会变为Modify
            entity.Name = user.Name;
            entity.Phone = user.Phone;
            entity.Sex = user.Sex;
            entity.IdCard = user.IdCard;
            entity.Email = user.Email;
            entity.Account = user.Account;
            entity.UpdateTime = DateTime.Now;
            //执行数据库操作
            if (await dbContext.SaveChangesAsync() > 0) return entity;
            throw new Exception("修改用户信息失败");
        }

        public async Task<bool> DeleteUser(int id)
        {
            var user = await dbContext.Users.SingleOrDefaultAsync(l => l.Id == id);
            dbContext.Users.Remove(user);//删除单个Leagues
            return await dbContext.SaveChangesAsync() > 0;
        }
        public async Task<decimal> RechargeWallet(RechargeRecordDTO rechargeRecord)
        {
            //1 开启事务
            using var tran = dbContext.Database.BeginTransaction();
            try
            {
                var user = await dbContext.Users.SingleOrDefaultAsync(l => l.Id == rechargeRecord.UserId);
                if (user is null) throw new Exception("用户不存在");
                user.Wallet += rechargeRecord.Price;

                await dbContext.RechargeRecords.AddAsync(new RechargeRecord()
                {
                    UserId = rechargeRecord.UserId,
                    RechargeNo = getting.OrderNo,
                    Mode = rechargeRecord.Mode,
                    Price = rechargeRecord.Price
                });
                if (await dbContext.SaveChangesAsync() > 0) await tran.CommitAsync();//提交事务
                return user.Wallet;
            }
            // 数据库抛出异常
            catch (SqlException)
            {
                await tran.RollbackAsync();
                throw new Exception("充值异常，请联系管理员");
            }
            catch (Exception)
            {
                await tran.RollbackAsync();
                throw new Exception("充值失败");
            }
        }

        public async Task<User> SelectUser()
        {
            return await dbContext.Users.SingleOrDefaultAsync(l => l.Id.Equals(UserId));
        }
        public async Task<User> SelectUserById(int id)
        {
            return await dbContext.Users.SingleOrDefaultAsync(l => l.Id.Equals(id));
        }

        public async Task<User> SelectUserByPhone(string phone)
        {
            return await dbContext.Users.Where(u => u.Phone.Equals(phone)).FirstOrDefaultAsync();
        }

        public async Task<User> SelectUserByPhoneAndPwd(string phone, string passWord)
        {
            return await dbContext.Users.Where(u => u.Phone.Equals(phone) && u.PassWord.Equals(passWord)).FirstOrDefaultAsync();
        }

        public async Task<PageList<User>> GetPageList(UserPageParameters parameters)
        {
            var query = dbContext.Users.AsQueryable();
            if (!string.IsNullOrWhiteSpace(parameters.Name))
            {
                query = query.Where(m => m.Name.Contains(parameters.Name.Trim()));
            }
            if (!string.IsNullOrWhiteSpace(parameters.Phone))
            {
                query = query.Where(m => m.Phone.Equals(parameters.Phone.Trim()));
            }
            return await PageList<User>.CreateAsync(query.OrderByDescending(o => o.CreateTime),
                parameters.Current,
                parameters.PageSize);
        }
        public async Task<dynamic> GetRechargePageList(ShopOrderPageParameters parameters)
        {
            var query = dbContext.RechargeRecords
                .Include(s => s.User)
                .AsQueryable();
            if (parameters.UserId != null && parameters.UserId > 0)
            {
                query = query.Where(m => m.UserId.Equals(parameters.UserId));
            }

            if (!string.IsNullOrWhiteSpace(parameters.OrderNo))
            {
                query = query.Where(m => m.RechargeNo.Contains(parameters.OrderNo.Trim()));
            }
            if (!string.IsNullOrWhiteSpace(parameters.UserName))
            {
                query = query.Where(m => m.User.Name.Contains(parameters.UserName.Trim()));
            }
            if (!string.IsNullOrWhiteSpace(parameters.UserPhone))
            {
                query = query.Where(m => m.User.Phone.Equals(parameters.UserPhone.Trim()));
            }
            var page = await PageList<RechargeRecord>.CreateAsync(query.OrderByDescending(o => o.CreateTime),
                parameters.Current,
                parameters.PageSize);
            var list = page.List.Select(x => new
            {
                x.Id,
                x.CreateTime,
                x.UpdateTime,
                x.RechargeNo,
                x.Mode,
                x.Price,
                User = new
                {
                    x.User.Id,
                    x.User.Code,
                    x.User.Name,
                    x.User.Phone
                }
            }).ToList();
            return new
            {
                page.Current,
                page.Total,
                page.PageSize,
                page.PageTotal,
                List = list
            };
        }

        #region 登录相关
        public int UserId
        {
            get
            {
                string uid = httpContext.User.FindFirstValue(JwtRegisteredClaimNames.Sid);
                if (string.IsNullOrEmpty(uid)) return 0;
                if (int.TryParse(uid, out int reval)) return reval;
                return reval;
            }
        }
        public async Task<User> UserLogin(string phone, string passWord)
        {
            //先查询出来，因为操作被追踪的数据
            var entity = await dbContext.Users.SingleOrDefaultAsync(u => u.Phone.Equals(phone) && u.PassWord.Equals(passWord));
            if (entity is null) throw new Exception("手机号或密码错误");
            //修改属性
            entity.Logins++;
            //执行数据库操作
            if (await dbContext.SaveChangesAsync() > 0) return entity;
            throw new Exception("登录失败");
        }
        public async Task<JwtAuthResult> SetUserLogin(User user)
        {
            if (user is null) throw new ArgumentNullException(nameof(user));
            string refreshToken = jwtAuth.GenerateRefreshToken(); //生成刷新token
            var cache = RedisCache(cache => cache);
            var expires = TimeSpan.FromDays(jwtAuth.Option.Expires);
            await cache.StringSetAsync(user.Code, JsonConvert.SerializeObject(user), expires);//保存用户信息

            var Claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Role,"webapp"),
                    new Claim(JwtRegisteredClaimNames.Sub, user.Code),
                    new Claim(JwtRegisteredClaimNames.Sid, user.Id.ToString())
                };
            return new JwtAuthResult
            {
                AccessToken = jwtAuth.GenerateToken(Claims),
                RefreshToken = refreshToken
            };
            throw new Exception("登录凭证设置失败");
        }
        #endregion
    }
}